package com.joysuch.wwyt.workflow.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.joysuch.wwyt.bp.entity.BpSystemVideo;
import com.joysuch.wwyt.bp.repository.BpSystemVideoDao;
import com.joysuch.wwyt.core.bean.BaseAttachmentBean;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.DateTimeUtil;
import com.joysuch.wwyt.core.common.util.JsonUtil;
import com.joysuch.wwyt.core.entity.BaseLoginAccount;
import com.joysuch.wwyt.core.service.UserShiroService;
import com.joysuch.wwyt.workflow.api.service.NewProcessService;
import com.joysuch.wwyt.workflow.bean.*;
import com.joysuch.wwyt.workflow.entity.WorkFlowLocationConfig;
import com.joysuch.wwyt.workflow.entity.WorkFlowProcess;
import com.joysuch.wwyt.workflow.entity.WorkFlowScreenType;
import com.joysuch.wwyt.workflow.entity.WorkFlowTask;
import com.joysuch.wwyt.workflow.repository.*;
import com.joysuch.wwyt.workflow.service.WorkFlowProcessService;
import com.joysuch.wwyt.workflow.service.WorkFlowScreenService;
import com.joysuch.wwyt.workflow.service.WorkFlowTaskProcessListService;
import com.joysuch.wwyt.workflow.utils.FormComponentUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Author:  feizhou
 * Date:    2022/9/23 10:13
 */
@Slf4j
@Service
public class WorkFlowScreenServiceImpl implements WorkFlowScreenService {
    @Autowired
    private WorkFlowScreenConfigDao workFlowScreenConfigDao;
    @Autowired
    private WorkFlowProcessDao workFlowProcessDao;
    @Autowired
    private WorkFlowProcessService workFlowProcessService;
    @Autowired
    private NewProcessService newProcessService;
    @Autowired
    private WorkFlowTaskDao workFlowTaskDao;
    @Autowired
    private WorkFlowScreenTypeDao workFlowScreenTypeDao;
    @Autowired
    private UserShiroService userShiroService;
    @Autowired
    private WorkFlowLocationConfigDao locationConfigDao;


    private final static String NEW_PROCESS_FIX = "new_";
    private final static String OLD_PROCESS_FIX = "old_";

    @Override
    public List<WorkFlowScreenBean> queryScreenDisplayWorkFlow(String columnCode) {
        List<WorkFlowScreenBean> result = Lists.newArrayList();

        // 找出旧的流程引擎所有大屏展示的作业票
        List<WorkFlowScreenBean> oldProcessList = searchOldWorkProcess(columnCode);
        // 找出新的流程引擎所有大屏展示的作业票
        List<WorkFlowScreenBean> newProcessList = searchNewWorkProcess(columnCode);

        result.addAll(oldProcessList);
        result.addAll(newProcessList);
        return result;
    }

    private List<WorkFlowScreenBean> searchNewWorkProcess(String columnCode) {
        return newProcessService.searchScreenDisplayProcess(columnCode);
    }

    private List<WorkFlowScreenBean> searchOldWorkProcess(String columnCode) {
        List<WorkFlowScreenBean> result = Lists.newArrayList();
        List<String> names = workFlowScreenConfigDao.findBizTypeNameListByEnvironmentFlagIsNull();
        WorkFlowScreenType workFlowScreenType = null;
        if (StringUtils.isNotEmpty(columnCode)) {
            workFlowScreenType = workFlowScreenTypeDao.findByCode(columnCode);
        }
        Long screenTypeId = workFlowScreenType == null ? -1L : workFlowScreenType.getId();
        List<WorkFlowProcess> workFlowProcesses = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(names)) {
            workFlowProcesses = workFlowProcessDao.findByBizTypeNames(names);
        }
        workFlowProcesses.forEach(process -> {
            WorkFlowLocationConfigBean<WorkFlowScreenConfigBean, WorkFlowRailAlarmConfigBean> configBean = workFlowProcessService.getLocationConfigByProcessId(process.getId());
            if (configBean == null) {
                return;
            }
            if (screenTypeId != -1L) {
                if (configBean.getScreenConfig() == null || configBean.getScreenConfig().getTypeId() == null
                        || screenTypeId.longValue() != configBean.getScreenConfig().getTypeId().longValue()) {
                    return;
                }
            }

            WorkFlowScreenBean workFlowScreenBean = new WorkFlowScreenBean();
            workFlowScreenBean.setId(OLD_PROCESS_FIX + process.getId());
            workFlowScreenBean.setName(process.getBizName());
            BaseAttachmentBean iconUpload = configBean.getIconUpload();
            workFlowScreenBean.setIconUrl(iconUpload == null ?  "" : "?id=" + iconUpload.getId());
            workFlowScreenBean.setMapShowName(configBean.getMapShowName());
            if (configBean.getScreenConfig() != null) {
                String windowName = configBean.getScreenConfig().getWindowName();
                workFlowScreenBean.setMapSmallScreenName(windowName);
                workFlowScreenBean.setMapSmallScreenEnable(configBean.getScreenConfig().isMapWindow());
            }
            result.add(workFlowScreenBean);
        });
        return result;
    }


    @Override
    public List<ProcessMapListBean> searchWorkFlowList(String processId, String serialNumber, String code, String bizTypeName) {
        List<ProcessMapListBean> result = Lists.newArrayList();
        // 查询旧流程引擎作业票
        List<ProcessMapListBean> oldWorkJobList = searchOldWorkFlow(processId, serialNumber, code, bizTypeName);
        // 查询新流程引擎作业票
        List<ProcessMapListBean> newWorkJobList = searchNewWorkFlow(processId, serialNumber, code);
        newWorkJobList.stream().forEach(inst -> {
            inst.getData().forEach(col -> {
                if ("创建人".equals(col.getLabel())) {
                    String val = col.getValue();
                    if (!Strings.isNullOrEmpty(val)) {
                        String realName = _getRealNameFromAccount(val);
                        if (!Strings.isNullOrEmpty(realName)) {
                            col.setValue(realName);
                        }
                    }
                }
            });
        });
        result.addAll(oldWorkJobList);
        result.addAll(newWorkJobList);

        return result;
    }

    private String _getRealNameFromAccount(String accountName) {
        try {
            BaseLoginAccount account = userShiroService.findByUserName(accountName);
            if (account != null) {
                return account.getBaseUser().getRealName();
            }
        } catch (Exception ignore) {
        }
        return null;
    }

    private List<ProcessMapListBean> searchNewWorkFlow(String processId, String serialNumber, String code) {
        List<ProcessMapListBean> result = Lists.newArrayList();
        if (StringUtils.isNotEmpty(processId) && processId.startsWith(OLD_PROCESS_FIX)) {
            return result;
        }
        if (StringUtils.isNotEmpty(processId)) {
            if (processId.startsWith(OLD_PROCESS_FIX)) {
                return result;
            } else {
                processId = processId.replace(NEW_PROCESS_FIX, "");
            }
        }
        List<ProcessMapListBean> list = newProcessService.searchScreenListWorkFlow(processId, serialNumber, code);
        if (list != null) {
            list.forEach(bean -> {
                bean.setProcessInstanceId(NEW_PROCESS_FIX + bean.getProcessInstanceId());
                bean.setProcessDefinitionId(NEW_PROCESS_FIX + bean.getProcessDefinitionId());
                if (bean.getLocation() != null) {
                    String location = bean.getLocation().replace("circlePoints", "points");
                    bean.setLocation(location);
                }
            });
            return list;
        }
        return result;
    }

    private List<ProcessMapListBean> searchOldWorkFlow(String processId, String serialNumber, String code, String bizTypeName) {
        WorkFlowScreenType workFlowScreenType = workFlowScreenTypeDao.findByCode(code);
        List<ProcessMapListBean> result = Lists.newArrayList();
        if (workFlowScreenType == null) {
            log.info("code {} not found screen type");
            return result;
        }

        Long workFlowProcessId2 = null;
        if (StringUtils.isNotEmpty(processId)) {
            if (processId.startsWith(NEW_PROCESS_FIX)) {
                return result;
            } else {
                workFlowProcessId2 = Long.parseLong(processId.replaceAll(OLD_PROCESS_FIX, ""));
            }
        }
        Long workFlowProcessId = workFlowProcessId2;
        WorkFlowSearchBean searchBean = new WorkFlowSearchBean();
        if ("work".equals(workFlowScreenType.getCode())) {
//            searchBean.setBizTypeName("作业票");
        } else {
//        searchBean.setBizTypeName(workFlowScreenType.getTypeName());
        }
        if (StringUtils.isNotEmpty(bizTypeName)) {
            searchBean.setBizTypeName(bizTypeName);
        }
        searchBean.setPage(0);
        searchBean.setSize(500);
        searchBean.setTypeFlag("page");
        searchBean.setTypeId(workFlowScreenType.getId());
        ResultBean resultBean = workFlowProcessService.workFlowScreen(searchBean);
        Object data = resultBean.getData();
        List<WorkFlowScreenDto> screenDtos = Lists.newArrayList();
        if (data != null) {
            if (data instanceof Collection) {
                ((Collection<?>) data).forEach(d -> {
                    screenDtos.add((WorkFlowScreenDto) d);
                });
            }
            if (data instanceof  WorkFlowScreenDto) {
                screenDtos.add((WorkFlowScreenDto) resultBean.getData());
            }
        }

        screenDtos.forEach(workFlowScreenList -> {
            if (workFlowScreenList == null) {
                return;
            }
            Map<Long, WorkFlowLocationConfigBean<WorkFlowScreenConfigBean, WorkFlowRailAlarmConfigBean>> map =
                    workFlowScreenList.getMap();

            List<WorkFlowTask> pageTaskList = workFlowScreenList.getPageTaskList();
            if (pageTaskList == null) {
                pageTaskList = workFlowScreenList.getTaskList();
            }
            if (pageTaskList == null || map == null) {
                return;
            }
            if (workFlowScreenList.getTypeCode() == null || !workFlowScreenList.getTypeCode().equals(workFlowScreenType.getCode())) {
                return;
            }
            Map<Long, WorkFlowProcess> processMap = Maps.newHashMap();
            for (Long workflowProcessId2 : map.keySet()) {
                if (workFlowProcessId != null && workFlowProcessId.longValue() != workflowProcessId2.longValue()) {
                    continue;
                }
                Optional<WorkFlowProcess> op = workFlowProcessDao.findById(workflowProcessId2);
                if (op.isPresent()) {
                    WorkFlowProcess process = op.get();
                    processMap.put(process.getId(), process);
                }
            }


            pageTaskList.forEach(task -> {
                WorkFlowLocationConfigBean<WorkFlowScreenConfigBean, WorkFlowRailAlarmConfigBean> configBean = map.get(task.getProcessId());

                if (StringUtils.isNotEmpty(serialNumber)) {
                    if (!task.getCode().contains(serialNumber.trim())) {
                        return;
                    }
                }
                WorkFlowProcess process = processMap.get(task.getProcessId());
                if (process == null) {
                    return;
                }
                JSONObject formKeyObj = JSONObject.parseObject(process.getFormKeyLabelJson());
                if (formKeyObj == null) {
                    return;
                }
                JSONObject formData = JSONObject.parseObject(task.getFormData());
                ProcessMapListBean processMapListBean = new ProcessMapListBean();
                processMapListBean.setName(process.getBizName());
                processMapListBean.setSerialNumber(task.getCode());
                List<CardLineBean> rowData = Lists.newArrayList();
                WorkFlowScreenConfigBean screenConfig = configBean.getScreenConfig();
                if (screenConfig != null) {
                    String tableColumn = screenConfig.getTableColumn();
                    if (StringUtils.isNotEmpty(tableColumn)) {
                        List<TableColumnConfigBean> tableColumnConfigBeans = JsonUtil.fromListJson(tableColumn, TableColumnConfigBean.class);

                        tableColumnConfigBeans.forEach(tableColumnConfigBean -> {
                            String key = formKeyObj.getString(tableColumnConfigBean.getLabel());
                            String val = "";
                            if (key != null) {
                                String string = formData.getString(key);
                                if (string != null) {
                                    val = string;
                                }
                            }
                            if ("类型".equals(tableColumnConfigBean.getLabel())) {
                                if (StringUtils.isEmpty(val)) {
                                    val = process.getBizName();
                                }
                            }
                            CardLineBean bean = new CardLineBean();
                            bean.setLabel(tableColumnConfigBean.getLabel());
                            bean.setValue(val);
                            rowData.add(bean);
                        });
                    }
                }
//                String mapLocationKey = workFlowTaskProcessListService.findProcessMapFormJsonKey(process.getFormJson());
                WorkFlowLocationConfig locationConfig = locationConfigDao.findFirstByProcessId(process.getId());
                String mapLocationKey = formKeyObj.getString("地图位置");
                if (locationConfig != null && StringUtils.isNotEmpty(locationConfig.getComponentKeyName())) {
                    mapLocationKey = locationConfig.getComponentKeyName();
                }
                String location = formData.getString(mapLocationKey);
                if (location != null) {
                    location = location.replace("circlePoints", "points");
                }
                processMapListBean.setLocation(location);
                processMapListBean.setData(rowData);
                processMapListBean.setProcessInstanceId((task.getParentId() != null && task.getParentId().intValue() != 0 ?
                        OLD_PROCESS_FIX + task.getParentId() + "" : OLD_PROCESS_FIX + task.getId()));
                processMapListBean.setProcessDefinitionId(OLD_PROCESS_FIX + task.getProcessId());
                result.add(processMapListBean);
            });
        });

        return result;
    }


    @Override
    public ProcessInstanceMapScreenDetail workFlowDetail(String processId) {
        if (StringUtils.isEmpty(processId)) {
            return emptyProcessInstanceMapScreenDetail();
        }
        if (processId.startsWith(NEW_PROCESS_FIX)) {
            processId = replacePreFix(processId, NEW_PROCESS_FIX);
            return searchNewWorkFlowDetail(processId);
        } else if (processId.startsWith(OLD_PROCESS_FIX)) {
            processId = replacePreFix(processId, OLD_PROCESS_FIX);
            return searchOldWorkFlowDetail(Long.parseLong(processId));
        }
        return emptyProcessInstanceMapScreenDetail();
    }

    @Autowired
    private BpSystemVideoDao bpSystemVideoDao;

    private ProcessInstanceMapScreenDetail searchNewWorkFlowDetail(String processInstanceId) {
        ProcessInstanceMapScreenDetail detailBean = newProcessService.getProcessInstanceDetail(processInstanceId);
        if (detailBean != null) {
            List<ComponentFormColumn> processInstanceDetail = detailBean.getProcessInstanceDetail();
            if (processInstanceDetail != null) {
                processInstanceDetail.forEach(col -> {
                    if ("group".equals(col.getType())) {
                        if (col.getColumn() != null) {
                            col.getColumn().forEach(subCol -> {
                                if ("dynamic".equals(subCol.getType())) {
                                    if (subCol.getChildren() != null && subCol.getChildren().getColumn() != null) {
                                        subCol.getChildren().getColumn().forEach(column -> {
                                            setNewProcessScreenMapColumnValue(column, processInstanceId);
                                        });
                                    }
                                } else {
                                    setNewProcessScreenMapColumnValue(subCol, processInstanceId);
                                    if (subCol.getColumn() != null) {
                                        subCol.getColumn().forEach(column -> setNewProcessScreenMapColumnValue(column, processInstanceId));
                                    }
                                }
                            });
                        }
                    }
                });
            }
        }
        return detailBean;
    }

    private void setNewProcessScreenMapColumnValue(ComponentFormColumn column, String processInstanceId) {
        if ("video".equals(column.getType())) {
            column.setType("videoSurveillance");
            if (StringUtils.isNotEmpty(column.getValue()) && StringUtils.isNumeric(column.getValue())) {
                long l = Long.parseLong(column.getValue());
                try {
                    BpSystemVideo one = bpSystemVideoDao.getOne(l);
                    if (one != null) {
                        VideoDetailBean videoDetailBean =  newProcessService.getVideoDetail(processInstanceId, column.getProp());
                        JSONObject jsonObject = new JSONObject();
                        String realTimeSite = one.getRealTimeSite();
                        jsonObject.put("realTimeSite", realTimeSite == null ? "" : realTimeSite);
                        jsonObject.put("name", one.getName());
                        jsonObject.put("id", one.getId());
                        BpSystemVideoDao bpSystemVideoDao = com.joysuch.wwyt.util.ApplicationContextHolder.getBean(BpSystemVideoDao.class);
                        Optional<BpSystemVideo> op = bpSystemVideoDao.findById(one.getId());
                        if (op.isPresent()) {
                            jsonObject.put("historySite", op.get().getHistorySite());
                            jsonObject.put("dateFormat", op.get().getDateFormat());
                            jsonObject.put("timeFormat", op.get().getTimeFormat());
                        }

                        if (videoDetailBean.isCanDisplay()) {
                            jsonObject.put("canDisplay", true);
                            if (videoDetailBean.getIsHistory() != null && videoDetailBean.getIsHistory()) {
                                jsonObject.put("isHistory", true);
                                if (videoDetailBean.getStartTime() != null) {
                                    jsonObject.put("startTime", DateTimeUtil.formatDate(videoDetailBean.getStartTime(), "yyyy-MM-dd HH:mm:ss"));
                                }
                                if (videoDetailBean.getEndTime() != null) {
                                    jsonObject.put("endTime", DateTimeUtil.formatDate(videoDetailBean.getEndTime(), "yyyy-MM-dd HH:mm:ss"));
                                }
                            } else {
                                jsonObject.put("isHistory", false);
                                jsonObject.put("tipMessage", videoDetailBean.getTipMessage());
                            }
                        } else {
                            jsonObject.put("canDisplay", false);
                        }
                        column.setValue(jsonObject.toJSONString());
                    }
                } catch (Exception e) {
                    log.error("find video error id {}", l, e);
                }
            }
        }
    }

    private ProcessInstanceMapScreenDetail searchOldWorkFlowDetail(long workTaskId) {
        ProcessInstanceMapScreenDetail processInstanceMapScreenDetail = emptyProcessInstanceMapScreenDetail();
        Optional<WorkFlowTask> op = workFlowTaskDao.findById(workTaskId);
        if (!op.isPresent()) {
            return processInstanceMapScreenDetail;
        }
        WorkFlowTask workFlowTask = op.get();

        Optional<WorkFlowProcess> processOp = workFlowProcessDao.findById(workFlowTask.getProcessId());
        if (!processOp.isPresent()) {
            return processInstanceMapScreenDetail;
        }
        WorkFlowProcess workFlowProcess = processOp.get();

        WorkFlowLocationConfigBean<WorkFlowScreenConfigBean, WorkFlowRailAlarmConfigBean> locationConfigBean = workFlowProcessService.getLocationConfigByProcessId(workFlowProcess.getId());
        if (locationConfigBean == null) {
            return processInstanceMapScreenDetail;
        }
        String processFormJson = workFlowProcess.getFormJson();
        JSONObject keyObj = JSONObject.parseObject(workFlowProcess.getFormKeyLabelJson());
        JSONObject formData = JSONObject.parseObject(workFlowTask.getFormData());
        List<ComponentFormColumn> processInstanceDetailList = Lists.newArrayList();
        WorkFlowScreenConfigBean screenConfig = locationConfigBean.getScreenConfig();
        if (StringUtils.isNotEmpty(screenConfig.getDetailColumn())) {
            List<WorkFlowScreenConfigBean.DetailColumn> detailColumns = JSONUtil.toList(screenConfig.getDetailColumn(), WorkFlowScreenConfigBean.DetailColumn.class);
            if (!org.springframework.util.CollectionUtils.isEmpty(detailColumns)) {
                List<ComponentFormColumn> componentFormColumns = FormComponentUtil.parseData(detailColumns, formData, processFormJson, workFlowTask.getCode());
                processInstanceMapScreenDetail.setProcessInstanceDetail(componentFormColumns);
            }
        }
//        processInstanceMapScreenDetail.setProcessInstanceDetail(processInstanceDetailList);
        if (!screenConfig.isMapWindow()) {
            return processInstanceMapScreenDetail;
        }
        String windowName = screenConfig.getWindowName();
        ProcessInstanceMapSmallScreenDetail smallScreenDetail = new ProcessInstanceMapSmallScreenDetail();
        smallScreenDetail.setName(windowName);
        smallScreenDetail.setEnable(true);

        List<CardLineBean> windowColumnDetailList = Lists.newArrayList();
        if (StringUtils.isNotEmpty(screenConfig.getWindowColumn())) {
            List<WorkFlowScreenConfigBean.SelectArr> windowColumns = JSONUtil.toList(screenConfig.getWindowColumn(), WorkFlowScreenConfigBean.SelectArr.class);
            if (windowColumns != null) {
                windowColumns.forEach(windowColumn -> {
                    CardLineBean cardLineBean = new CardLineBean();
                    cardLineBean.setLabel(windowColumn.getLabel());
                    cardLineBean.setType(windowColumn.getType());
                    String value = "";
                    if (StringUtils.isNotEmpty(windowColumn.getKey())) {
                        value = formData.getString(windowColumn.getKey());
                    } else {
                        String key = keyObj.getString(windowColumn.getLabel());
                        if (key != null) {
                            value = formData.getString(key);
                        }
                    }
                    if ("类型".equals(windowColumn.getLabel())) {
                        if (StringUtils.isEmpty(value)) {
                            value = workFlowProcess.getBizName();
                        }
                    }
                    cardLineBean.setValue(setValueOrDefault(value, ""));
                    windowColumnDetailList.add(cardLineBean);
                });
            }
        }
        smallScreenDetail.setData(windowColumnDetailList);

        processInstanceMapScreenDetail.setSmallScreenDetail(smallScreenDetail);
        return processInstanceMapScreenDetail;
    }

    private String setValueOrDefault(String value, String defaultValue) {
        return value == null ? defaultValue : value;
    }

    private ProcessInstanceMapScreenDetail emptyProcessInstanceMapScreenDetail() {
        ProcessInstanceMapScreenDetail processInstanceMapScreenDetail = new ProcessInstanceMapScreenDetail();
        List<ComponentFormColumn> processInstanceDetail = Lists.newArrayList();
        processInstanceMapScreenDetail.setProcessInstanceDetail(processInstanceDetail);
        ProcessInstanceMapSmallScreenDetail smallScreenDetail = new ProcessInstanceMapSmallScreenDetail();
        smallScreenDetail.setEnable(false);
        smallScreenDetail.setData(Lists.newArrayList());
        processInstanceMapScreenDetail.setSmallScreenDetail(smallScreenDetail);
        return processInstanceMapScreenDetail;
    }

    private String replacePreFix(String processId, String preFix) {
        return processId.replace(preFix, "");
    }

}
